package cn.com.chengmandian.bike.service.impl;

import cn.com.chengmandian.agent.utils.AuthUtil;
import cn.com.chengmandian.bike.dto.MemberComboRecordDto;
import cn.com.chengmandian.bike.model.*;
import cn.com.chengmandian.bike.mapper.EbMemberComboRecordMapper;
import cn.com.chengmandian.bike.model.vo.AgentPayInfo;
import cn.com.chengmandian.bike.model.vo.MemberComboRecordVo;
import cn.com.chengmandian.bike.service.*;
import cn.com.chengmandian.bike.utils.AliPayUtil;
import cn.com.chengmandian.bike.utils.WxPayUtil;
import cn.com.chengmandian.core.redis.service.RedisService;
import cn.com.chengmandian.core.web.exceptions.BadResponseException;
import cn.com.chengmandian.core.web.pojo.AgentInfo;
import cn.com.chengmandian.core.web.pojo.AjaxResult;
import cn.com.chengmandian.core.web.utils.LocalMapUtil;
import cn.com.chengmandian.core.web.utils.SpringContextUtil;
import cn.com.chengmandian.service.bike.BikePayClient;
import cn.com.chengmandian.service.bike.constants.*;
import cn.com.chengmandian.service.bike.form.BikeRefundForm;
import cn.com.chengmandian.service.bike.form.MemberComboRefundForm;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;

/**
 * <p>
 * 电单车运营系统-会员购买记录 服务实现类
 * </p>
 *
 * @author AutoGenerator
 * @since 2023-08-28
 */
@Service
public class EbMemberComboRecordServiceImpl extends ServiceImpl<EbMemberComboRecordMapper, EbMemberComboRecord> implements IEbMemberComboRecordService {

    @Autowired
    private RedisService redisService;
    @Autowired
    private IEbMemberPayService memberPayService;
    @Autowired
    private IEbRefundInfoService refundInfoService;
    @Autowired
    private IEbMemberWalletService memberWalletService;
    @Autowired
    private IEbMemberInfoService memberInfoService;
    @Autowired
    private IEbMessageInfoService messageInfoService;
    @Autowired
    private IEbMemberComboInfoService memberComboInfoService;

    @Autowired
    private IEbAgentInfoService agentInfoService;

    @Autowired
    private IEbPayAccountService payAccountService;

    @Autowired
    private BikePayClient bikePayClient;

    @Autowired
    private IEbMemberMoneyService memberMoneyService;

    @Override
    public IPage<EbMemberComboRecord> pages(MemberComboRecordDto dto, Integer memberId) {
        Page page = Page.of(dto.getPageNumber(), dto.getPageSize());
        return baseMapper.getPage(page, dto, memberId);
    }

    @Override
    public MemberComboRecordVo getMemberAvailableRecord(Integer areaId, Integer memberId) {
        MemberComboRecordVo vo = new MemberComboRecordVo();
        EbMemberComboRecord record = this.getOne(new LambdaQueryWrapper<EbMemberComboRecord>()
                .eq(EbMemberComboRecord::getAreaId, areaId)
                .eq(EbMemberComboRecord::getMemberId, memberId)
                .gt(EbMemberComboRecord::getExpireTime, DateUtil.date())
                .le(EbMemberComboRecord::getActiveTime, DateUtil.date())
                .and(i-> i.isNull(EbMemberComboRecord::getRefundMoney).or().le(EbMemberComboRecord::getRefundMoney, BigDecimal.ZERO))
                .orderByAsc(EbMemberComboRecord::getExpireTime)
                .last("limit 0,1"));
        if(record == null) return null;
        BeanUtils.copyProperties(record, vo);
        vo.setEndExpireTime(record.getExpireTime());

        EbMemberInfo ebMemberInfo = memberInfoService.getById(record.getMemberId());
        vo.setMemberName(StrUtil.isBlank(ebMemberInfo.getName())? ebMemberInfo.getNickname() : ebMemberInfo.getName());

        EbMemberComboRecord endRecord = this.getOne(new LambdaQueryWrapper<EbMemberComboRecord>()
                .eq(EbMemberComboRecord::getAreaId, areaId)
                .eq(EbMemberComboRecord::getMemberId, memberId)
                .ne(EbMemberComboRecord::getId, record.getId())
                .gt(EbMemberComboRecord::getExpireTime, DateUtil.date())
                .and(i-> i.isNull(EbMemberComboRecord::getRefundMoney).or().le(EbMemberComboRecord::getRefundMoney, BigDecimal.ZERO))
                .orderByDesc(EbMemberComboRecord::getExpireTime)
                .last("limit 0,1"));
        if(endRecord != null){
            vo.setEndExpireTime(endRecord.getExpireTime());
        }
        return vo;
    }

    @Override
    public EbMemberComboRecord createRecord(Integer payId, Integer memberId, Integer comboId, BigDecimal money) {
        EbMemberComboInfo memberComboInfo = memberComboInfoService.getById(comboId);
        Assert.notNull(memberComboInfo, "套餐不存在");

        Date activeTime = DateUtil.date();
        MemberComboRecordVo memberComboRecord = this.getMemberAvailableRecord(memberComboInfo.getAreaId(), memberId);
        if(memberComboRecord != null){
            activeTime = DateUtil.offset(memberComboRecord.getEndExpireTime(), DateField.SECOND, 1);
        }
        EbMemberComboRecord ebMemberComboRecord = new EbMemberComboRecord();
        ebMemberComboRecord.setAreaId(memberComboInfo.getAreaId());
        ebMemberComboRecord.setMemberId(memberId);
        ebMemberComboRecord.setPayId(payId);
        ebMemberComboRecord.setComboId(memberComboInfo.getId());
        ebMemberComboRecord.setComboName(memberComboInfo.getName());
        ebMemberComboRecord.setComboDays(memberComboInfo.getDays());
        ebMemberComboRecord.setMoney(money);
        ebMemberComboRecord.setActiveTime(activeTime);
        ebMemberComboRecord.setExpireTime(DateUtil.offsetDay(activeTime, memberComboInfo.getDays()));
        this.save(ebMemberComboRecord);
        return ebMemberComboRecord;
    }

    @Override
    public void refund(MemberComboRefundForm form) {
        String key = SpringContextUtil.getAppName().toUpperCase() + ":BIKE:REFUND:MEMBER:COMBO:" + form.getPayId();
        try {
            if (redisService.hasKey(key)) {
                throw new BadResponseException("该会员卡订单正在退款中,请稍等....");
            }
            redisService.set(key, form.getPayId(), 3);
            // 会员卡信息
            EbMemberComboRecord memberComboRecord = this.getOne(new LambdaQueryWrapper<EbMemberComboRecord>()
                    .eq(EbMemberComboRecord::getPayId,form.getPayId())
                    .last("limit 0,1"));
            Assert.notNull(memberComboRecord,"会员卡信息不存在！");

            // 支付记录
            EbMemberPay ebMemberPay = memberPayService.getById(form.getPayId());
            Assert.notNull(ebMemberPay, "支付记录信息不存在");
            Assert.isTrue(ebMemberPay.getMoney().compareTo(BigDecimal.ZERO) > 0, "金额不支持退款!");
            Assert.isTrue(form.getMoney().compareTo((ebMemberPay.getMoney().subtract(ObjectUtil.isNull(ebMemberPay.getRefundMoney()) ? BigDecimal.ZERO : ebMemberPay.getRefundMoney()))) <= 0,"退款金额不得大于剩余总金额");

            AgentInfo agentInfo = LocalMapUtil.getAgentInfo();
            String notifyUrl = "http://"+ agentInfo.getDomain() + "/api/v1/bikeuser/notify";
            if (ebMemberPay.getPayChannel().equals(PayChannelEnum.WALLET.getCode())) {
                // 生成退款支付记录
                EbRefundInfo ebRefundInfo = refundInfoService.createRefund(ebMemberPay.getAreaId(),ebMemberPay.getMemberId(), ebMemberPay.getId(), ebMemberPay.getId(), ebMemberPay.getFeeRatio(), form.getMoney(), RefundBusinessTypeEnum.COMBO, RefundChannelEnum.MONEY, form.getReason(),form.getLoginUser());
                memberWalletService.refundMoney(memberComboRecord.getAreaId(),ebMemberPay.getMemberId(), MemberMoneyBusinessTypeEnum.MEMBER_COMBO_REFUND, memberComboRecord.getId(), form.getMoney(),BigDecimal.ZERO);
                this.refundSuccess(ebRefundInfo, ebMemberPay.getPayNo());
            } else if (ebMemberPay.getPayChannel().equals(PayChannelEnum.WXPAY.getCode())) {
                // 生成退款支付记录
                EbRefundInfo ebRefundInfo = refundInfoService.createRefund(ebMemberPay.getAreaId(),ebMemberPay.getMemberId(), ebMemberPay.getId(), ebMemberPay.getId(), ebMemberPay.getFeeRatio(), form.getMoney(), RefundBusinessTypeEnum.COMBO, RefundChannelEnum.WX, form.getReason(),form.getLoginUser());
                EbMemberInfo ebMemberInfo = memberInfoService.getById(ebMemberPay.getMemberId());
                EbAgentInfo info = agentInfoService.getById(agentInfo.getId());
                if (info.getPayAccountId() != null) {
                    EbPayAccount payAccount = payAccountService.getPayAccount(info.getPayAccountId(), ebMemberPay.getMemberId());
                    if (payAccount != null) {
                        BikeRefundForm refundForm = new BikeRefundForm();
                        refundForm.setBusinessType(RefundBusinessTypeEnum.COMBO);
                        refundForm.setMemberPayId(ebMemberPay.getId());
                        refundForm.setMoney(ebRefundInfo.getMoney());
                        refundForm.setRefundNo(ebRefundInfo.getRefundNo());
                        refundForm.setAlipayUserId(ebMemberInfo.getAlipayUserId());
                        refundForm.setAlipayOpenId(ebMemberInfo.getAlipayOpenId());
                        AjaxResult result = bikePayClient.refund(ebMemberPay.getAgentId(), refundForm);
                        Assert.isTrue(result.isSuccess(), result.getMessage());
                        return;
                    }
                }
                try {
                    WxPayUtil.refund(
                            agentInfo.getWxMchId(),
                            ebRefundInfo.getRefundNo(),
                            ebMemberPay.getPayNo(),
                            "会员卡订单退款",
                            ebRefundInfo.getMoney(),
                            ebMemberPay.getMoney(),
                            agentInfo.getWxKeyPath(),
                            agentInfo.getWxCertPath(),
                            notifyUrl + "/wx/member/combo/refund/" + LocalMapUtil.getAgentInfo().getId());
                } catch (Exception ex) {
                    log.error("微信退款失败", ex);
                    throw new BadResponseException("微信退款失败:" + ex.getMessage());
                }
            } else if (ebMemberPay.getPayChannel().equals(PayChannelEnum.ALIPAY.getCode())) {
                // 生成退款支付记录
                EbRefundInfo ebRefundInfo = refundInfoService.createRefund(ebMemberPay.getAreaId(),ebMemberPay.getMemberId(), ebMemberPay.getId(), ebMemberPay.getId(), ebMemberPay.getFeeRatio(), form.getMoney(), RefundBusinessTypeEnum.COMBO, RefundChannelEnum.ALI, form.getReason(),form.getLoginUser());
                EbMemberInfo ebMemberInfo = memberInfoService.getById(ebMemberPay.getMemberId());
                EbAgentInfo info = agentInfoService.getById(agentInfo.getId());
                if (info.getPayAccountId() != null) {
                    EbPayAccount payAccount = payAccountService.getPayAccount(info.getPayAccountId(), ebMemberPay.getMemberId());
                    if (payAccount != null) {
                        BikeRefundForm refundForm = new BikeRefundForm();
                        refundForm.setBusinessType(RefundBusinessTypeEnum.COMBO);
                        refundForm.setMemberPayId(ebMemberPay.getId());
                        refundForm.setMoney(ebRefundInfo.getMoney());
                        refundForm.setRefundNo(ebRefundInfo.getRefundNo());
                        refundForm.setAlipayUserId(ebMemberInfo.getAlipayUserId());
                        refundForm.setAlipayOpenId(ebMemberInfo.getAlipayOpenId());
                        AjaxResult result = bikePayClient.refund(ebMemberPay.getAgentId(), refundForm);
                        Assert.isTrue(result.isSuccess(), result.getMessage());
                        return;
                    }
                }
                try {
                    Map<String, String> res = AliPayUtil.getTradeRefundRequestParam(
                            agentInfo.getAliMiniAppid(),
                            ebMemberInfo.getAlipayUserId(),
                            ebMemberInfo.getAlipayOpenId(),
                            "会员卡订单退款",
                            ebMemberPay.getPayNo(),
                            ebRefundInfo.getRefundNo(),
                            ebRefundInfo.getMoney(),
                            agentInfo.getAliPayPrivateKey(),
                            agentInfo.getAliPayCertPath(),
                            agentInfo.getAliPayPublicCertPath(),
                            agentInfo.getAliPayRootCertPath());
                    ebRefundInfo.setChannelNo(res.get("trade_no"));
                    this.refundSuccess(ebRefundInfo, ebMemberPay.getPayNo());
                } catch (Exception ex) {
                    log.error("支付宝退款失败", ex);
                    throw new BadResponseException("支付宝退款失败:" + ex.getMessage());
                }
            }
        }catch (Exception ex){
            redisService.del(key);
            throw new BadResponseException(ex.getMessage());
        }
    }

    @Override
    public void refundSuccess(EbRefundInfo ebRefundInfo, String payNo) {
        Assert.isTrue(ebRefundInfo.getBusinessType().equals(RefundBusinessTypeEnum.COMBO.getCode()),"退款类型错误");
        // 支付记录
        EbMemberPay ebMemberPay = memberPayService.getByNo(payNo);
        Assert.notNull(ebMemberPay,"支付记录不存在！");

        // 会员卡信息
        EbMemberComboRecord memberComboRecord = this.getOne(new LambdaQueryWrapper<EbMemberComboRecord>()
                .eq(EbMemberComboRecord::getPayId,ebMemberPay.getId())
                .last("limit 0,1"));
        Assert.notNull(memberComboRecord,"会员卡信息不存在！");

        BigDecimal refundMoney = (ObjectUtil.isNull(ebMemberPay.getRefundMoney()) ? BigDecimal.ZERO : ebMemberPay.getRefundMoney()).add(ebRefundInfo.getMoney());
        MemberPayStatusEnum memberPayStatusEnum = MemberPayStatusEnum.PAID;
        if(refundMoney.compareTo(ebMemberPay.getMoney()) >=0){
            memberPayStatusEnum = MemberPayStatusEnum.REFUND;
        }
        this.update(new LambdaUpdateWrapper<EbMemberComboRecord>()
                .set(EbMemberComboRecord::getRefundMoney,refundMoney)
                .eq(EbMemberComboRecord::getId,memberComboRecord.getId()));

        memberPayService.update(new LambdaUpdateWrapper<EbMemberPay>()
                .set(EbMemberPay::getStatus,memberPayStatusEnum.getCode())
                .set(EbMemberPay::getRefundMoney,refundMoney)
                .set(EbMemberPay::getRefundTime,new Date())
                .eq(EbMemberPay::getId,ebMemberPay.getId()));

        // 退款信息
        refundInfoService.update(new LambdaUpdateWrapper<EbRefundInfo>()
                .set(EbRefundInfo::getStatus,RefundStatusEnum.REFUND_OK.getCode())
                .set(EbRefundInfo::getCompleteTime,new Date())
                .set(EbRefundInfo::getRefundMoney,ebRefundInfo.getMoney())
                .set(EbRefundInfo::getChannelNo,ebRefundInfo.getChannelNo())
                .eq(EbRefundInfo::getId,ebRefundInfo.getId()));

        String key = SpringContextUtil.getAppName().toUpperCase() + ":BIKE:REFUND:MEMBER:COMBO:" + ebMemberPay.getId();
        redisService.del(key);

        messageInfoService.addSys(ebMemberPay.getMemberId(), MessageChannelEnum.SYS,
                "尊敬的用户，您于 "  + DateUtil.now() + " 退款成功" + ebRefundInfo.getMoney().doubleValue() + "元，感谢您的支持与信任！",
                null, MessageBusinessTypeEnum.COMBO, null);

        // 非余额支付生成流水
        if(!PayChannelEnum.WALLET.getCode().equals(ebMemberPay.getPayChannel())) {
            memberMoneyService.createPay(ebMemberPay.getAreaId(), ebMemberPay.getMemberId(), MemberMoneyBusinessTypeEnum.MEMBER_COMBO_REFUND, ebMemberPay.getBusinessId(), MemberMoneyOperationTypeEnum.SERVICE, ebRefundInfo.getMoney());
        }
    }
}
